#include "m_mysqli.h"
#include "helper.h"
#include "common.h"
#include "agent.h"
#include "segment.h"
#include "span.h"
// #include "skywalking_client.h"

#include "ext/mysqli/php_mysqli_structs.h"
#include "ext/mysqlnd/mysqlnd_libmysql_compat.h"
#include "ext/mysqlnd/mysqlnd_structs.h"
#include "ext/mysqlnd/mysqlnd_enum_n_def.h"

#include <map>
#include <string>
#include <vector>
#include <unistd.h>
#include <malloc.h>

void monitorpro_mysqli_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name)
{
    uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
    if (arg_count)
    {
        // 初始化参数
        std::string ip = "127.0.0.1";
        std::string port = "3306";
        std::string addr = "";
        std::string sql = "";
        std::string err = "";
        std::string query = "";

        zval *mysql_link = ZEND_CALL_ARG(execute_data, 1);

        if (Z_TYPE_P(mysql_link) != IS_OBJECT)
        {
            return;
        }

        zval *z_sql = ZEND_CALL_ARG(execute_data, 2);
        MY_MYSQL *mysql;
        MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_INITIALIZED);

        // 获取数据库地址
        MYSQLND_STRING hostname = mysql->mysql->data->hostname;
        std::string host = hostname.s;
        int port_i = mysql->mysql->data->port;
        std::vector<std::string> ipport = split(host, ":");
        ip = ipport.size() > 0 ? ipport[0] : "";
        port = std::to_string(port_i);
        addr = ip + ":" + port;
        std::vector<std::string>().swap(ipport);
        malloc_trim(0);

        // 获取数据库操作
        std::string sql_string = z_sql == nullptr ? "unknown" : Z_STRVAL_P(z_sql);
        std::vector<std::string> sql_str = split(sql_string, " ");
        sql = sql_str[0];
        query = class_name + "->" + function_name + "::" + sql;
        std::vector<std::string>().swap(sql_str);

        // 获取错误信息
        if (IS_FALSE == Z_TYPE_P(return_value))
        {
            err = mysql->mysql->data->error_info->error;
        }

        // SkywalkingClient *skywalking_client = SkywalkingClient::newInstance();
        // Segment *segment = skywalking_client->getSegment();
        // auto *span = segment->createSpan(SkySpanType::Exit, SkySpanLayer::Database, 8003);
        // span->setOperationName(class_name + "->" + function_name);
        // span->addTag("db.statement", sql_string);
        // span->setPeer(addr);
        // span->setStartTime(start_micro_time / 1000);
        // span->setEndTime(end_micro_time / 1000);

        push_db_data(addr, query, err, end_micro_time - start_micro_time);
    }
}

void monitorpro_mysqli_obj_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name)
{
    // 初始化参数
    std::string ip = "127.0.0.1";
    std::string port = "3306";
    std::string addr = "";
    std::string sql = "";
    std::string err = "";
    std::string query = "";
    std::string sql_string = "";

    // 获取数据库连接地址
    zval mysql_link = execute_data->This;
    MY_MYSQL *mysql;
    MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_INITIALIZED);
    MYSQLND_STRING hostname = mysql->mysql->data->hostname;
    std::string host = hostname.s;
    int port_i = mysql->mysql->data->port;
    std::vector<std::string> ipport = split(host, ":");
    ip = ipport.size() > 0 ? ipport[0] : "";
    port = std::to_string(port_i);
    addr = ip + ":" + port;
    std::vector<std::string>().swap(ipport);
    malloc_trim(0);

    // 获取sql
    uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
    if (arg_count)
    {
        zval *z_sql = ZEND_CALL_ARG(execute_data, 1);
        sql_string = z_sql == nullptr ? "unknown" : Z_STRVAL_P(z_sql);
        std::vector<std::string> sql_str = split(sql_string, " ");
        sql = sql_str[0];
        query = class_name + "->" + function_name + "::" + sql;
        std::vector<std::string>().swap(sql_str);
        malloc_trim(0);
    }

    // 获取错误信息
    if (IS_FALSE == Z_TYPE_P(return_value))
    {
        err = mysql->mysql->data->error_info->error;
    }

    // SkywalkingClient *skywalking_client = SkywalkingClient::newInstance();
    // Segment *segment = skywalking_client->getSegment();
    // auto *span = segment->createSpan(SkySpanType::Exit, SkySpanLayer::Database, 8003);
    // span->setOperationName(class_name + "->" + function_name);
    // span->addTag("db.statement", sql_string);
    // span->setPeer(addr);
    // span->setStartTime(start_micro_time / 1000);
    // span->setEndTime(end_micro_time / 1000);

    push_db_data(addr, query, err, end_micro_time - start_micro_time);
}

void monitorpro_mysqli_stmt_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name)
{
    uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
    if (arg_count)
    {
        // 初始化参数
        std::string ip = "127.0.0.1";
        std::string port = "3306";
        std::string addr = "";
        std::string sql = "";
        std::string err = "";
        std::string query = "";

        MY_STMT *stmt;
        zval *mysql_stmt;

        mysql_stmt = ZEND_CALL_ARG(execute_data, 1);
        if (Z_TYPE_P(mysql_stmt) != IS_OBJECT)
        {
            return;
        }

        MYSQLI_FETCH_RESOURCE_STMT(stmt, mysql_stmt, MYSQLI_STATUS_VALID);

        // 获取数据库地址
        std::string host = stmt->stmt->data->conn->hostname.s;
        int port_i = stmt->stmt->data->conn->port;
        std::vector<std::string> ipport = split(host, ":");
        ip = ipport.size() > 0 ? ipport[0] : "";
        port = std::to_string(port_i);
        addr = ip + ":" + port;
        std::vector<std::string>().swap(ipport);
        malloc_trim(0);

        // 获取数据库操作
        int type = stmt->stmt->data->conn->last_query_type;
        uint64_t last_insert_id = stmt->stmt->data->conn->upsert_status->last_insert_id;
        switch (type)
        {
        case QUERY_UPSERT:
            sql = last_insert_id > 0 ? "insert" : "update";
            break;
        case QUERY_SELECT:
            sql = "select";
            break;
        case QUERY_LOAD_LOCAL:
            sql = "load";
            break;
        default:
            sql = "unknown";
            break;
        }
        query = class_name + "->" + function_name + "::" + sql;

        // 获取错误信息
        if (IS_FALSE == Z_TYPE_P(return_value))
        {
            err = stmt->stmt->data->conn->error_info->error;
        }

        // SkywalkingClient *skywalking_client = SkywalkingClient::newInstance();
        // Segment *segment = skywalking_client->getSegment();
        // auto *span = segment->createSpan(SkySpanType::Exit, SkySpanLayer::Database, 8003);
        // span->setOperationName(class_name + "->" + function_name);
        // span->addTag("db.statement", sql);
        // span->setPeer(addr);
        // span->setStartTime(start_micro_time / 1000);
        // span->setEndTime(end_micro_time / 1000);

        push_db_data(addr, query, err, end_micro_time - start_micro_time);
    }
}

void monitorpro_mysqli_stmt_obj_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name)
{
    // 初始化参数
    std::string ip = "127.0.0.1";
    std::string port = "3306";
    std::string addr = "";
    std::string sql = "";
    std::string err = "";
    std::string query = "";

    MY_STMT *stmt;
    zval mysql_stmt;

    // 获取数据库连接地址
    mysql_stmt = execute_data->This;
    MYSQLI_FETCH_RESOURCE_STMT(stmt, &mysql_stmt, MYSQLI_STATUS_VALID);
    std::string host = stmt->stmt->data->conn->hostname.s;
    int port_i = stmt->stmt->data->conn->port;
    std::vector<std::string> ipport = split(host, ":");
    ip = ipport.size() > 0 ? ipport[0] : "";
    port = std::to_string(port_i);
    addr = ip + ":" + port;
    std::vector<std::string>().swap(ipport);
    malloc_trim(0);

    // 获取数据库操作
    int type = stmt->stmt->data->conn->last_query_type;
    uint64_t last_insert_id = stmt->stmt->data->conn->upsert_status->last_insert_id;
    switch (type)
    {
    case QUERY_UPSERT:
        sql = last_insert_id > 0 ? "insert" : "update";
        break;
    case QUERY_SELECT:
        sql = "select";
        break;
    case QUERY_LOAD_LOCAL:
        sql = "load";
        break;
    default:
        sql = "unknown";
        break;
    }
    query = class_name + "->" + function_name + "::" + sql;

    // 获取错误信息
    if (IS_FALSE == Z_TYPE_P(return_value))
    {
        err = stmt->stmt->data->conn->error_info->error;
    }

    // SkywalkingClient *skywalking_client = SkywalkingClient::newInstance();
    // Segment *segment = skywalking_client->getSegment();
    // auto *span = segment->createSpan(SkySpanType::Exit, SkySpanLayer::Database, 8003);
    // span->setOperationName(class_name + "->" + function_name);
    // span->addTag("db.statement", sql);
    // span->setPeer(addr);
    // span->setStartTime(start_micro_time / 1000);
    // span->setEndTime(end_micro_time / 1000);

    push_db_data(addr, query, err, end_micro_time - start_micro_time);
}